namespace SimpleWebServices.Web.UI.Html
{
    using System;
    using System.Runtime.Serialization;
    using System.Security.Permissions;
    using System.Text;
    using System.Text.RegularExpressions;
    using SimpleWebServices.Xml;

    [Serializable]
    public sealed class Name : IComparable, IComparable<Name>, IEquatable<Name>, ISerializable
    {
        private static readonly Regex _regex = new Regex(@"^[a-zA-Z]{1}?[a-zA-Z0-9-_:.]*$");
        private string _token;

        public Name(string token)
        {
            this.Token = token;
        }

        private Name(SerializationInfo info, StreamingContext context)
        {
            this._token = info.GetString("_token");
        }

        public string Token
        {
            get
            {
                return this._token;
            }

            set
            {
                if (null == value)
                {
                    throw new ArgumentNullException("value");
                }
                else if (0 == value.Length)
                {
                    throw new ArgumentOutOfRangeException("value");
                }
                else if (!_regex.IsMatch(value))
                {
                    throw new FormatException(value + " is invalid. Name tokens must begin with a letter ([A-Za-z]) and may be followed by any number of letters, digits ([0-9]), hyphens (\"-\"), underscores (\"_\"), colons (\":\"), and periods (\".\").");
                }

                this._token = value;
            }
        }

        public static bool operator ==(Name obj, Name comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator !=(Name obj, Name comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = !obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator <(Name obj, Name comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = 0 > obj.CompareTo(comparand);
            }

            return result;
        }

        public static bool operator >(Name obj, Name comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = false;
            }
            else
            {
                result = 0 < obj.CompareTo(comparand);
            }

            return result;
        }

        public static implicit operator string(Name name)
        {
            return (null == name) ? null : name.Token;
        }

        public static implicit operator Name(string token)
        {
            return (null == token) ? null as Name : new Name(token);
        }

        public static Name Convert(DateTime value)
        {
            StringBuilder buffer = new StringBuilder();

            byte offset = 49;
            foreach (char c in value.ToXmlString().RemoveNonnumericCharacters())
            {
                buffer.Append((char)((byte)c + offset));
            }

            return buffer.ToString();
        }

        public int CompareTo(Name other)
        {
            int result = 0;

            if (null == other)
            {
                result = 1;
            }
            else
            {
                result = string.CompareOrdinal(this.Token, other.Token);
            }

            return result;
        }

        public int CompareTo(object obj)
        {
            int result = 0;

            if (null == obj)
            {
                result = 1;
            }
            else
            {
                Name cast = obj as Name;
                if (null == cast)
                {
                    throw new ArgumentOutOfRangeException("obj");
                }

                result = string.CompareOrdinal(this.Token, cast.Token);
            }

            return result;
        }

        public bool Equals(Name other)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, other))
            {
                if (object.ReferenceEquals(this, other))
                {
                    result = true;
                }
                else
                {
                    result = 0 == this.CompareTo(other);
                }
            }

            return result;
        }

        public override bool Equals(object obj)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, obj))
            {
                if (object.ReferenceEquals(this, obj))
                {
                    result = true;
                }
                else
                {
                    Name cast = obj as Name;

                    if (!object.ReferenceEquals(null, cast))
                    {
                        result = 0 == this.CompareTo(cast);
                    }
                }
            }

            return result;
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public T ToSimpleLink<T>()
            where T : XLink
        {
            return this.ToSimpleLink<T>(null as string);
        }

        public T ToSimpleLink<T>(string title)
            where T : XLink
        {
            T result = Activator.CreateInstance<T>();

            result.Href = "#" + this.Token;
            result.Title = title;
            result.Type = "simple";

            return result;
        }

        public override string ToString()
        {
            return this.Token;
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            info.AddValue("_token", this._token);
        }
    }
}